package com.chujian.framework.db.crypto;

import cn.hutool.core.collection.CollUtil;
import com.chujian.framework.annotation.db.GuessIt;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ：yj
 * @date ：2020-09-16
 * mybatis 加解密实现
 */
@Slf4j
public class MybatisCrypto implements IMybatisCrypto {

    private final DefaultDataCrypto crypto;
    private Map<Class, List<Field>> cache = new ConcurrentHashMap<>();

    public MybatisCrypto(DefaultDataCrypto crypto) {
        this.crypto = crypto;
    }

    @Override
    public <T> T encrypto(T parameterObject) {
        if (parameterObject == null) return null;
        if (needCrypto(parameterObject)) {
            getCachedFieldList(parameterObject.getClass())
                    .forEach(field -> {
                        try {
                            field.setAccessible(true);
                            Object value = field.get(parameterObject);
                            field.set(parameterObject, crypto.enCrypto(value));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    });
        }
        return parameterObject;
    }

    @Override
    public <T> T decrypto(T result) {
        if (result == null) return null;

        if (result instanceof List) { // 列表
            log.debug("处理列表返回结果");
            List<?> list = (List) result;
            if (CollUtil.isNotEmpty(list) && needCrypto(list.get(0))) {
                for (Object o : list) {
                    decrypt(o);
                }
            }
        } else { // 对象
            log.debug("处理对象返回结果");
            if (needCrypto(result)) decrypt(result);
        }
        return result;
    }

    private List<Field> getCachedFieldList(Class<?> objectClass) {
        if (cache.containsKey(objectClass)) {
            return cache.get(objectClass);
        }
        synchronized (cache) {
            if (cache.containsKey(objectClass)) {
                return cache.get(objectClass);
            }
            List<Field> fields = FieldUtils.getFieldsListWithAnnotation(objectClass, GuessIt.class);
            cache.put(objectClass, fields);
        }
        return cache.get(objectClass);
    }


    // 对象是否需要加解密
    private boolean needCrypto(Object object) {
        Class<?> objectClass = object.getClass();
        GuessIt guess = AnnotationUtils.findAnnotation(objectClass, GuessIt.class);
        return guess != null;
    }

    private void decrypt(Object obj) {
        if (obj == null) return;
        getCachedFieldList(obj.getClass())
                .forEach(field -> {
                    try {
                        field.setAccessible(true);
                        Object value = field.get(obj);
                        field.set(obj, crypto.deCrypto((String) value));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
    }


}
